package com.idream.marker.handle;

import com.idream.marker.anno.Marker;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Name;
import lombok.core.AST;
import lombok.core.AnnotationValues;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.JavacTreeMaker;
import lombok.javac.handlers.JavacHandlerUtil;
import org.mangosdk.spi.ProviderFor;

import static lombok.javac.handlers.JavacHandlerUtil.*;

/**
 * Handles the {@link Marker}
 */
@ProviderFor(JavacAnnotationHandler.class)
public class HandleMarker extends JavacAnnotationHandler<Marker> {

    private final static Class markerClass = org.slf4j.Marker.class;
    private final static String markerName = "org.slf4j.Marker";
    private final static String markerFactory = "org.slf4j.MarkerFactory";

    @Override
    public void handle(AnnotationValues<Marker> annotationValues,
                       com.sun.tools.javac.tree.JCTree.JCAnnotation jcAnnotation,
                       JavacNode javacNode) {

//        HandleLog.processAnnotation(LoggingFramework.SLF4J, annotation, annotationNode);

        processAnnotation(annotationValues, javacNode, annotationValues.getInstance().value());
    }

    public void processAnnotation(AnnotationValues<Marker> annotation, JavacNode annotationNode, String fieldName) {

        JavacNode typeNode = annotationNode.up(); // 2
        if (!checkFieldInject(annotationNode, typeNode, fieldName)) {
            return;
        }
        JCTree.JCVariableDecl fieldDecl = createField(annotation, annotationNode, typeNode);
//        injectFieldAndMarkGenerated(typeNode, fieldDecl); // 3
        JavacHandlerUtil.injectFieldSuppressWarnings(typeNode, fieldDecl);

    }

    private JCTree.JCVariableDecl createField(AnnotationValues<Marker> annotation, JavacNode annotationNode, JavacNode typeNode) {
        JavacTreeMaker maker = typeNode.getTreeMaker();
        Name name = ((JCTree.JCClassDecl) typeNode.get()).name;
        JCTree.JCFieldAccess loggingType = maker.Select(maker.Ident(name), typeNode.toName("class"));

        JCTree.JCExpression loggerType = chainDotsString(typeNode, markerName);
        JCTree.JCExpression factoryMethod = chainDotsString(typeNode, markerFactory);

        JCTree.JCExpression loggerName;
        String topic = annotation.getInstance().value();
        if (topic == null || topic.trim().length() == 0) { // 1
            loggerName = loggingType;
        } else {
            loggerName = maker.Literal(topic);
        }

        JCTree.JCMethodInvocation factoryMethodCall = maker.Apply(List.<JCTree.JCExpression>nil(), factoryMethod, loggerName != null ? List.of(loggerName) : List.<JCTree.JCExpression>nil());

        return recursiveSetGeneratedBy(maker.VarDef(
                maker.Modifiers(Flags.PRIVATE | Flags.FINAL | Flags.STATIC),
                typeNode.toName(topic), loggerType, factoryMethodCall), annotationNode.get(), typeNode.getContext());
    }

    private boolean checkFieldInject(JavacNode annotationNode, JavacNode typeNode, String fieldName) {
        if (typeNode.getKind() != AST.Kind.TYPE) {
            annotationNode.addError("@Marker is legal only on types.");
            return false;
        }
        if ((((JCTree.JCClassDecl) typeNode.get()).mods.flags & Flags.INTERFACE) != 0) {
            annotationNode.addError("@Marker is legal only on classes and enums.");
            return false;
        }
        if (fieldExists(fieldName, typeNode) != JavacHandlerUtil.MemberExistsResult.NOT_EXISTS) {
            annotationNode.addWarning("Field '" + fieldName + "' already exists.");
            return false;
        }
        return true;
    }

}
